package com.ailink.sap.xml;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

public class XmlDeserialize {
    private Class<?> _class;
    private String origin;
    private Object instance;

    public XmlDeserialize(Class<?> _class, String origin) {
        this.origin = origin;
        this._class = _class;
    }

    public <T> T deserialize() {
        //寻找根
        XmlNode rootXmlNode = _class.getAnnotation(XmlNode.class);
        if (rootXmlNode == null) {
            throw new XmlException(_class + "不存在根节点XmlNode");
        }

        String xml = origin.trim();

        //去除头
        int location0 = xml.indexOf("<?xml");
        if (location0 == 0) {
            int location1 = xml.indexOf("?>", location0);
            if (location1 > location0) {
                xml = xml.substring(location1 + "?>".length()).trim();
            }
        }

        xml = findXmlNodeValue(rootXmlNode, xml);
        //创建类
        try {
            this.instance = _class.newInstance();
        } catch (Exception e) {
            throw new XmlException(e);
        }

        if (StringUtils.isEmpty(xml)) {
            return null;
        }

        search(instance, xml);

        return (T) instance;
    }

    private void search(Object instance, String xml) {
        Field[] fields = instance.getClass().getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {
            Field field = fields[i];
            XmlNode xmlNode = field.getAnnotation(XmlNode.class);
            CdData cdData = field.getAnnotation(CdData.class);
            if (xmlNode == null) {
                continue;
            }
            field.setAccessible(true);
            Class<?> type = field.getType();

            Object valueField;
            try {
                if (type.equals(List.class)) {
                    valueField = new ArrayList<>();
                } else {
                    valueField = type.newInstance();
                }
            } catch (InstantiationException e) {
                e.printStackTrace();
                continue;
            } catch (IllegalAccessException e) {
                e.printStackTrace();
                continue;
            }

            String name;
            if (!StringUtils.isEmpty(xmlNode.prefix())) {
                name = xmlNode.prefix() + Xml.SPLIT + xmlNode.name();
            } else {
                name = xmlNode.name();
            }

            if (valueField instanceof List) {
                try {
                    field.set(instance, valueField);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                    continue;
                }
            }

            if (valueField instanceof List) {
                List<String> xmlList = findXmlList(name, xml);
                Type genericType = field.getGenericType();
                ParameterizedType parameterizedType = (ParameterizedType) genericType;
                Class actualClass = (Class) parameterizedType.getActualTypeArguments()[0];
                for (String valueXml : xmlList) {
                    try {
                        Object valueOfList = actualClass.newInstance();
                        ((List) field.get(instance)).add(valueOfList);
                        search(valueOfList, valueXml);
                    } catch (InstantiationException e) {
                        e.printStackTrace();
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            } else {
                String valueXml = findSingleXmlValue(xml, name);
                if (valueXml == null) continue;

                if (cdData != null) {
                    int locationCdData0 = valueXml.indexOf(Xml.CDDATA_LEFT);
                    if (locationCdData0 == 0) {
                        locationCdData0 = locationCdData0 + Xml.CDDATA_LEFT.length();
                        int locationCdData1 = valueXml.lastIndexOf(Xml.CDDATA_RIGHT);
                        if (locationCdData0 > 0 && locationCdData1 > locationCdData0) {
                            valueXml = valueXml.substring(locationCdData0, locationCdData1);
                        }
                    }
                }

                try {
                    field.set(instance, valueField);
                    if (valueField instanceof String) {
                        field.set(instance, valueXml);
                        continue;
                    }
                    search(valueField, valueXml);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private String findSingleXmlValue(String xml, String name) {
        int location0 = xml.indexOf(Xml.LEFT + name);
        if (location0 < 0) {
            return null;
        }

        int location1 = xml.indexOf(Xml.LEFT_END + name + Xml.RIGHT, location0);
        if (location1 < 0) {
            return null;
        }

        int location2 = xml.indexOf(Xml.RIGHT, location0);
        if (location2 < 0) {
            throw new XmlException("XML格式非法");
        }
        String valueXml = xml.substring(location2 + 1, location1).trim();
        return valueXml;
    }

    private List<String> findXmlList(String name, String xml) {
        List<String> xmlList = new ArrayList<>();

        int start = 0;
        while (true) {
            int location0 = xml.indexOf(Xml.LEFT + name, start);
            if (location0 < 0) {
                break;
            }

            int location1 = xml.indexOf(Xml.LEFT_END + name + Xml.RIGHT, location0);
            if (location1 < 0) {
                break;
            }

            int location2 = xml.indexOf(Xml.RIGHT, location0);
            if (location2 < 0) {
                throw new XmlException("XML格式非法");
            }
            start = location1 + (Xml.LEFT_END + name + Xml.RIGHT).length();
            String valueXml = xml.substring(location2 + 1, location1).trim();
            xmlList.add(valueXml);
        }

        return xmlList;
    }


    private String findXmlNodeValue(XmlNode xmlNode, String xml) {
        String name = xmlNode.prefix() + ":" + xmlNode.name();
        int left = xml.indexOf(Xml.LEFT + name);
        if (left != 0) {
            throw new XmlException("xml格式不合法");
        }
        left = xml.indexOf(Xml.RIGHT);
        int right = xml.indexOf(Xml.LEFT_END + name + Xml.RIGHT);
        if (left < 0 || right < 0) {
            throw new XmlException("xml格式不合法");
        }

        String value = xml.substring(left + 1, right);
        return value.trim();
    }
}
